home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkMenubutton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-24  |  35.6 KB  |  1,144 lines

  1. /* 
  2.  * tkMenubutton.c --
  3.  *
  4.  *    This module implements button-like widgets that are used
  5.  *    to invoke pull-down menus.
  6.  *
  7.  * Copyright 1990 Regents of the University of California.
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMenubutton.c,v 1.33 92/08/21 16:21:47 ouster Exp $ SPRITE (Berkeley)";
  19. #endif
  20.  
  21. #include "tkConfig.h"
  22. #include "default.h"
  23. #include "tkInt.h"
  24.  
  25. /*
  26.  * A data structure of the following type is kept for each
  27.  * widget managed by this file:
  28.  */
  29.  
  30. typedef struct {
  31.     Tk_Window tkwin;        /* Window that embodies the widget.  NULL
  32.                  * means that the window has been destroyed
  33.                  * but the data structures haven't yet been
  34.                  * cleaned up.*/
  35.     Tcl_Interp *interp;        /* Interpreter associated with menu button. */
  36.     char *menuName;        /* Name of menu associated with widget (used
  37.                  * to generate post and unpost commands).
  38.                  * Malloc-ed. */
  39.     Tk_Uid varName;        /* Name of variable associated with collection
  40.                  * of menu bars:  used to allow scanning of
  41.                  * menus.   Also used as identifier for
  42.                  * menu group. */
  43.  
  44.     /*
  45.      * Information about what's displayed in the menu button:
  46.      */
  47.  
  48.     char *text;            /* Text to display in button (malloc'ed)
  49.                  * or NULL. */
  50.     int textLength;        /* # of characters in text. */
  51.     int underline;        /* Index of character to underline. */
  52.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  53.                  * If non-NULL, button displays the contents
  54.                  * of this variable. */
  55.     Pixmap bitmap;        /* Bitmap to display or None.  If not None
  56.                  * then text and textVar and underline
  57.                  * are ignored. */
  58.  
  59.     /*
  60.      * Information used when displaying widget:
  61.      */
  62.  
  63.     Tk_Uid state;        /* State of button for display purposes:
  64.                  * normal, active, or disabled. */
  65.     Tk_3DBorder normalBorder;    /* Structure used to draw 3-D
  66.                  * border and background when window
  67.                  * isn't active.  NULL means no such
  68.                  * border exists. */
  69.     Tk_3DBorder activeBorder;    /* Structure used to draw 3-D
  70.                  * border and background when window
  71.                  * is active.  NULL means no such
  72.                  * border exists. */
  73.     int borderWidth;        /* Width of border. */
  74.     int relief;            /* 3-d effect: TK_RELIEF_RAISED, etc. */
  75.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  76.     XColor *normalFg;        /* Foreground color in normal mode. */
  77.     XColor *activeFg;        /* Foreground color in active mode.  NULL
  78.                  * means use normalFg instead. */
  79.     XColor *disabledFg;        /* Foreground color when disabled.  NULL
  80.                  * means use normalFg with a 50% stipple
  81.                  * instead. */
  82.     GC normalTextGC;        /* GC for drawing text in normal mode. */
  83.     GC activeTextGC;        /* GC for drawing text in active mode (NULL
  84.                  * means use normalTextGC). */
  85.     Pixmap gray;        /* Pixmap for displaying disabled text/icon if
  86.                  * disabledFg is NULL. */
  87.     GC disabledGC;        /* Used to produce disabled effect.  If
  88.                  * disabledFg isn't NULL, this GC is used to
  89.                  * draw button text or icon.  Otherwise
  90.                  * text or icon is drawn with normalGC and
  91.                  * this GC is used to stipple background
  92.                  * across it. */
  93.     int leftBearing;        /* Amount text sticks left from its origin,
  94.                  * in pixels. */
  95.     int rightBearing;        /* Amount text sticks right from its origin. */
  96.     int width, height;        /* If > 0, these specify dimensions to request
  97.                  * for window, in characters for text and in
  98.                  * pixels for bitmaps.  In this case the actual
  99.                  * size of the text string or bitmap is
  100.                  * ignored in computing desired window size. */
  101.     int padX, padY;        /* Extra space around text or bitmap (pixels
  102.                  * on each side). */
  103.     Tk_Anchor anchor;        /* Where text/bitmap should be displayed
  104.                  * inside window region. */
  105.  
  106.     /*
  107.      * Miscellaneous information:
  108.      */
  109.  
  110.     Cursor cursor;        /* Current cursor for window, or None. */
  111.     int flags;            /* Various flags;  see below for
  112.                  * definitions. */
  113. } MenuButton;
  114.  
  115. /*
  116.  * Flag bits for buttons:
  117.  *
  118.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  119.  *                has already been queued to redraw
  120.  *                this window.
  121.  * POSTED:            Non-zero means that the menu associated
  122.  *                with this button has been posted (typically
  123.  *                because of an active button press).
  124.  */
  125.  
  126. #define REDRAW_PENDING        1
  127. #define POSTED            2
  128.  
  129. /*
  130.  * Information used for parsing configuration specs:
  131.  */
  132.  
  133. static Tk_ConfigSpec configSpecs[] = {
  134.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  135.     DEF_MENUBUTTON_ACTIVE_BG_COLOR, Tk_Offset(MenuButton, activeBorder),
  136.     TK_CONFIG_COLOR_ONLY},
  137.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  138.     DEF_MENUBUTTON_ACTIVE_BG_MONO, Tk_Offset(MenuButton, activeBorder),
  139.     TK_CONFIG_MONO_ONLY},
  140.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  141.     DEF_MENUBUTTON_ACTIVE_FG_COLOR, Tk_Offset(MenuButton, activeFg),
  142.     TK_CONFIG_COLOR_ONLY},
  143.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  144.     DEF_MENUBUTTON_ACTIVE_FG_MONO, Tk_Offset(MenuButton, activeFg),
  145.     TK_CONFIG_MONO_ONLY},
  146.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  147.     DEF_MENUBUTTON_ANCHOR, Tk_Offset(MenuButton, anchor), 0},
  148.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  149.     DEF_MENUBUTTON_BG_COLOR, Tk_Offset(MenuButton, normalBorder),
  150.     TK_CONFIG_COLOR_ONLY},
  151.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  152.     DEF_MENUBUTTON_BG_MONO, Tk_Offset(MenuButton, normalBorder),
  153.     TK_CONFIG_MONO_ONLY},
  154.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  155.     (char *) NULL, 0, 0},
  156.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  157.     (char *) NULL, 0, 0},
  158.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  159.     DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
  160.     TK_CONFIG_NULL_OK},
  161.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  162.     DEF_MENUBUTTON_BORDER_WIDTH, Tk_Offset(MenuButton, borderWidth), 0},
  163.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  164.     DEF_MENUBUTTON_CURSOR, Tk_Offset(MenuButton, cursor),
  165.     TK_CONFIG_NULL_OK},
  166.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  167.     "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
  168.     Tk_Offset(MenuButton, disabledFg),
  169.     TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  170.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  171.     "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO,
  172.     Tk_Offset(MenuButton, disabledFg),
  173.     TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  174.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  175.     (char *) NULL, 0, 0},
  176.     {TK_CONFIG_FONT, "-font", "font", "Font",
  177.     DEF_MENUBUTTON_FONT, Tk_Offset(MenuButton, fontPtr), 0},
  178.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  179.     DEF_MENUBUTTON_FG, Tk_Offset(MenuButton, normalFg), 0},
  180.     {TK_CONFIG_INT, "-height", "height", "Height",
  181.     DEF_MENUBUTTON_HEIGHT, Tk_Offset(MenuButton, height), 0},
  182.     {TK_CONFIG_STRING, "-menu", "menu", "Menu",
  183.     DEF_MENUBUTTON_MENU, Tk_Offset(MenuButton, menuName), 0},
  184.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  185.     DEF_MENUBUTTON_PADX, Tk_Offset(MenuButton, padX), 0},
  186.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  187.     DEF_MENUBUTTON_PADY, Tk_Offset(MenuButton, padY), 0},
  188.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  189.     DEF_MENUBUTTON_RELIEF, Tk_Offset(MenuButton, relief), 0},
  190.     {TK_CONFIG_UID, "-state", "state", "State",
  191.     DEF_MENUBUTTON_STATE, Tk_Offset(MenuButton, state), 0},
  192.     {TK_CONFIG_STRING, "-text", "text", "Text",
  193.     DEF_MENUBUTTON_TEXT, Tk_Offset(MenuButton, text), 0},
  194.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  195.     DEF_MENUBUTTON_TEXT_VARIABLE, Tk_Offset(MenuButton, textVarName),
  196.     TK_CONFIG_NULL_OK},
  197.     {TK_CONFIG_INT, "-underline", "underline", "Underline",
  198.     DEF_MENUBUTTON_UNDERLINE, Tk_Offset(MenuButton, underline), 0},
  199.     {TK_CONFIG_UID, "-variable", "variable", "Variable",
  200.     DEF_MENUBUTTON_VARIABLE, Tk_Offset(MenuButton, varName), 0},
  201.     {TK_CONFIG_INT, "-width", "width", "Width",
  202.     DEF_MENUBUTTON_WIDTH, Tk_Offset(MenuButton, width), 0},
  203.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  204.     (char *) NULL, 0, 0}
  205. };
  206.  
  207. /*
  208.  * Forward declarations for procedures defined later in this file:
  209.  */
  210.  
  211. static void        ComputeMenuButtonGeometry _ANSI_ARGS_((
  212.                 MenuButton *mbPtr));
  213. static void        MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
  214.                 XEvent *eventPtr));
  215. static char *        MenuButtonTextVarProc _ANSI_ARGS_((
  216.                 ClientData clientData, Tcl_Interp *interp,
  217.                 char *name1, char *name2, int flags));
  218. static char *        MenuButtonVarProc _ANSI_ARGS_((ClientData clientData,
  219.                 Tcl_Interp *interp, char *name1, char *name2,
  220.                 int flags));
  221. static int        MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  222.                 Tcl_Interp *interp, int argc, char **argv));
  223. static int        ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
  224.                 MenuButton *mbPtr, int argc, char **argv,
  225.                 int flags));
  226. static void        DestroyMenuButton _ANSI_ARGS_((ClientData clientData));
  227. static void        DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
  228.  
  229. /*
  230.  *--------------------------------------------------------------
  231.  *
  232.  * Tk_MenubuttonCmd --
  233.  *
  234.  *    This procedure is invoked to process the "button", "label",
  235.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  236.  *    user documentation for details on what it does.
  237.  *
  238.  * Results:
  239.  *    A standard Tcl result.
  240.  *
  241.  * Side effects:
  242.  *    See the user documentation.
  243.  *
  244.  *--------------------------------------------------------------
  245.  */
  246.  
  247. int
  248. Tk_MenubuttonCmd(clientData, interp, argc, argv)
  249.     ClientData clientData;    /* Main window associated with
  250.                  * interpreter. */
  251.     Tcl_Interp *interp;        /* Current interpreter. */
  252.     int argc;            /* Number of arguments. */
  253.     char **argv;        /* Argument strings. */
  254. {
  255.     register MenuButton *mbPtr;
  256.     Tk_Window tkwin = (Tk_Window) clientData;
  257.     Tk_Window new;
  258.  
  259.     if (argc < 2) {
  260.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  261.         argv[0], " pathName ?options?\"", (char *) NULL);
  262.     return TCL_ERROR;
  263.     }
  264.  
  265.     /*
  266.      * Create the new window.
  267.      */
  268.  
  269.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  270.     if (new == NULL) {
  271.     return TCL_ERROR;
  272.     }
  273.  
  274.     /*
  275.      * Initialize the data structure for the button.
  276.      */
  277.  
  278.     mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton));
  279.     mbPtr->tkwin = new;
  280.     mbPtr->interp = interp;
  281.     mbPtr->menuName = NULL;
  282.     mbPtr->varName = NULL;
  283.     mbPtr->text = NULL;
  284.     mbPtr->underline = -1;
  285.     mbPtr->textVarName = NULL;
  286.     mbPtr->bitmap = None;
  287.     mbPtr->state = tkNormalUid;
  288.     mbPtr->normalBorder = NULL;
  289.     mbPtr->activeBorder = NULL;
  290.     mbPtr->borderWidth = 0;
  291.     mbPtr->relief = TK_RELIEF_FLAT;
  292.     mbPtr->fontPtr = NULL;
  293.     mbPtr->normalFg = NULL;
  294.     mbPtr->activeFg = NULL;
  295.     mbPtr->disabledFg = NULL;
  296.     mbPtr->normalTextGC = NULL;
  297.     mbPtr->activeTextGC = NULL;
  298.     mbPtr->gray = None;
  299.     mbPtr->disabledGC = NULL;
  300.     mbPtr->cursor = None;
  301.     mbPtr->flags = 0;
  302.  
  303.     Tk_SetClass(mbPtr->tkwin, "Menubutton");
  304.     Tk_CreateEventHandler(mbPtr->tkwin, ExposureMask|StructureNotifyMask,
  305.         MenuButtonEventProc, (ClientData) mbPtr);
  306.     Tcl_CreateCommand(interp, Tk_PathName(mbPtr->tkwin), MenuButtonWidgetCmd,
  307.         (ClientData) mbPtr, (void (*)()) NULL);
  308.     if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) {
  309.     Tk_DestroyWindow(mbPtr->tkwin);
  310.     return TCL_ERROR;
  311.     }
  312.  
  313.     interp->result = Tk_PathName(mbPtr->tkwin);
  314.     return TCL_OK;
  315. }
  316.  
  317. /*
  318.  *--------------------------------------------------------------
  319.  *
  320.  * MenuButtonWidgetCmd --
  321.  *
  322.  *    This procedure is invoked to process the Tcl command
  323.  *    that corresponds to a widget managed by this module.
  324.  *    See the user documentation for details on what it does.
  325.  *
  326.  * Results:
  327.  *    A standard Tcl result.
  328.  *
  329.  * Side effects:
  330.  *    See the user documentation.
  331.  *
  332.  *--------------------------------------------------------------
  333.  */
  334.  
  335. static int
  336. MenuButtonWidgetCmd(clientData, interp, argc, argv)
  337.     ClientData clientData;    /* Information about button widget. */
  338.     Tcl_Interp *interp;        /* Current interpreter. */
  339.     int argc;            /* Number of arguments. */
  340.     char **argv;        /* Argument strings. */
  341. {
  342.     register MenuButton *mbPtr = (MenuButton *) clientData;
  343.     int result = TCL_OK;
  344.     int length;
  345.     char c;
  346.  
  347.     if (argc < 2) {
  348.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  349.         " option ?arg arg ...?\"", (char *) NULL);
  350.     return TCL_ERROR;
  351.     }
  352.     Tk_Preserve((ClientData) mbPtr);
  353.     c = argv[1][0];
  354.     length = strlen(argv[1]);
  355.     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
  356.     if (argc > 2) {
  357.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  358.             argv[0], " activate\"", (char *) NULL);
  359.         goto error;
  360.     }
  361.     if (mbPtr->state != tkDisabledUid) {
  362.         mbPtr->state = tkActiveUid;
  363.         Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
  364.         goto redisplay;
  365.     }
  366.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  367.     if (argc == 2) {
  368.         result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
  369.             (char *) mbPtr, (char *) NULL, 0);
  370.     } else if (argc == 3) {
  371.         result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
  372.             (char *) mbPtr, argv[2], 0);
  373.     } else {
  374.         result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2,
  375.             TK_CONFIG_ARGV_ONLY);
  376.     }
  377.     } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)) {
  378.     if (argc > 2) {
  379.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  380.             argv[0], " deactivate\"", (char *) NULL);
  381.         goto error;
  382.     }
  383.     if (mbPtr->state != tkDisabledUid) {
  384.         mbPtr->state = tkNormalUid;
  385.         Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
  386.         goto redisplay;
  387.     }
  388.     } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) {
  389.     if (argc > 2) {
  390.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  391.             argv[0], " post\"", (char *) NULL);
  392.         goto error;
  393.     }
  394.     if ((mbPtr->flags & POSTED) || (mbPtr->menuName == NULL)
  395.         || (mbPtr->state == tkDisabledUid)) {
  396.         goto done;
  397.     }
  398.  
  399.     /*
  400.      * Store the name of the posted menu into the associated variable.
  401.      * This will cause any other menu posted via that variable to
  402.      * unpost itself and will cause this menu to post itself.
  403.      */
  404.  
  405.     Tcl_SetVar(interp, mbPtr->varName, Tk_PathName(mbPtr->tkwin),
  406.         TCL_GLOBAL_ONLY);
  407.     } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) {
  408.     if (argc > 2) {
  409.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  410.             argv[0], " unpost\"", (char *) NULL);
  411.         goto error;
  412.     }
  413.  
  414.     /*
  415.      * The one-liner below looks simple, but it isn't.  This code
  416.      * does the right thing even if this menu isn't posted anymore,
  417.      * but some other variable associated with the same variable
  418.      * is posted instead:  it unposts whatever is posted.  This
  419.      * approach is necessary because at present ButtonRelease
  420.      * events go to the menu button where the mouse button was
  421.      * first pressed;  this may not be the same menu button that's
  422.      * currently active.
  423.      */
  424.     Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY);
  425.     } else {
  426.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  427.         "\":  must be activate, configure, deactivate, ",
  428.         "post, or unpost", (char *) NULL);
  429.     goto error;
  430.     }
  431.     done:
  432.     Tk_Release((ClientData) mbPtr);
  433.     return result;
  434.  
  435.     redisplay:
  436.     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
  437.     Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  438.     mbPtr->flags |= REDRAW_PENDING;
  439.     }
  440.     goto done;
  441.  
  442.     error:
  443.     Tk_Release((ClientData) mbPtr);
  444.     return TCL_ERROR;
  445. }
  446.  
  447. /*
  448.  *----------------------------------------------------------------------
  449.  *
  450.  * DestroyMenuButton --
  451.  *
  452.  *    This procedure is invoked to recycle all of the resources
  453.  *    associated with a button widget.  It is invoked as a
  454.  *    when-idle handler in order to make sure that there is no
  455.  *    other use of the button pending at the time of the deletion.
  456.  *
  457.  * Results:
  458.  *    None.
  459.  *
  460.  * Side effects:
  461.  *    Everything associated with the widget is freed up.
  462.  *
  463.  *----------------------------------------------------------------------
  464.  */
  465.  
  466. static void
  467. DestroyMenuButton(clientData)
  468.     ClientData clientData;    /* Info about button widget. */
  469. {
  470.     register MenuButton *mbPtr = (MenuButton *) clientData;
  471.     if (mbPtr->menuName != NULL) {
  472.         ckfree(mbPtr->menuName);
  473.     }
  474.     if (mbPtr->varName != NULL) {
  475.     Tcl_UntraceVar(mbPtr->interp, mbPtr->varName,
  476.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  477.         MenuButtonVarProc, (ClientData) mbPtr);
  478.     }
  479.     if (mbPtr->text != NULL) {
  480.     ckfree(mbPtr->text);
  481.     }
  482.     if (mbPtr->textVarName != NULL) {
  483.     Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
  484.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  485.         MenuButtonTextVarProc, (ClientData) mbPtr);
  486.     ckfree(mbPtr->textVarName);
  487.     }
  488.     if (mbPtr->bitmap != None) {
  489.     Tk_FreeBitmap(mbPtr->bitmap);
  490.     }
  491.     if (mbPtr->normalBorder != NULL) {
  492.     Tk_Free3DBorder(mbPtr->normalBorder);
  493.     }
  494.     if (mbPtr->activeBorder != NULL) {
  495.     Tk_Free3DBorder(mbPtr->activeBorder);
  496.     }
  497.     if (mbPtr->fontPtr != NULL) {
  498.     Tk_FreeFontStruct(mbPtr->fontPtr);
  499.     }
  500.     if (mbPtr->normalFg != NULL) {
  501.     Tk_FreeColor(mbPtr->normalFg);
  502.     }
  503.     if (mbPtr->activeFg != NULL) {
  504.     Tk_FreeColor(mbPtr->activeFg);
  505.     }
  506.     if (mbPtr->disabledFg != NULL) {
  507.     Tk_FreeColor(mbPtr->disabledFg);
  508.     }
  509.     if (mbPtr->normalTextGC != None) {
  510.     Tk_FreeGC(mbPtr->normalTextGC);
  511.     }
  512.     if (mbPtr->activeTextGC != None) {
  513.     Tk_FreeGC(mbPtr->activeTextGC);
  514.     }
  515.     if (mbPtr->gray != None) {
  516.     Tk_FreeBitmap(mbPtr->gray);
  517.     }
  518.     if (mbPtr->disabledGC != None) {
  519.     Tk_FreeGC(mbPtr->disabledGC);
  520.     }
  521.     if (mbPtr->cursor != None) {
  522.     Tk_FreeCursor(mbPtr->cursor);
  523.     }
  524.     ckfree((char *) mbPtr);
  525. }
  526.  
  527. /*
  528.  *----------------------------------------------------------------------
  529.  *
  530.  * ConfigureMenuButton --
  531.  *
  532.  *    This procedure is called to process an argv/argc list, plus
  533.  *    the Tk option database, in order to configure (or
  534.  *    reconfigure) a menubutton widget.
  535.  *
  536.  * Results:
  537.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  538.  *    returned, then interp->result contains an error message.
  539.  *
  540.  * Side effects:
  541.  *    Configuration information, such as text string, colors, font,
  542.  *    etc. get set for mbPtr;  old resources get freed, if there
  543.  *    were any.  The menubutton is redisplayed.
  544.  *
  545.  *----------------------------------------------------------------------
  546.  */
  547.  
  548. static int
  549. ConfigureMenuButton(interp, mbPtr, argc, argv, flags)
  550.     Tcl_Interp *interp;        /* Used for error reporting. */
  551.     register MenuButton *mbPtr;    /* Information about widget;  may or may
  552.                  * not already have values for some fields. */
  553.     int argc;            /* Number of valid entries in argv. */
  554.     char **argv;        /* Arguments. */
  555.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  556. {
  557.     XGCValues gcValues;
  558.     GC newGC;
  559.     unsigned long mask;
  560.     int result;
  561.     Tk_Uid oldGroup;
  562.     char *value;
  563.  
  564.     /*
  565.      * Eliminate any existing traces on variables monitored by the button.
  566.      */
  567.  
  568.     if (mbPtr->varName != NULL) {
  569.     Tcl_UntraceVar(interp, mbPtr->varName,
  570.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  571.         MenuButtonVarProc, (ClientData) mbPtr);
  572.     }
  573.     if (mbPtr->textVarName != NULL) {
  574.     Tcl_UntraceVar(interp, mbPtr->textVarName,
  575.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  576.         MenuButtonTextVarProc, (ClientData) mbPtr);
  577.     }
  578.  
  579.     oldGroup = mbPtr->varName;
  580.     result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs,
  581.         argc, argv, (char *) mbPtr, flags);
  582.     if (oldGroup != mbPtr->varName) {
  583.     Tk_UnshareEvents(mbPtr->tkwin, oldGroup);
  584.     Tk_ShareEvents(mbPtr->tkwin, mbPtr->varName);
  585.     }
  586.     if (result != TCL_OK) {
  587.     return TCL_ERROR;
  588.     }
  589.  
  590.     /*
  591.      * A few options need special processing, such as setting the
  592.      * background from a 3-D border, or filling in complicated
  593.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  594.      */
  595.  
  596.     if (mbPtr->state == tkActiveUid) {
  597.     Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
  598.     } else {
  599.     Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
  600.     if ((mbPtr->state != tkNormalUid) && (mbPtr->state != tkDisabledUid)) {
  601.         Tcl_AppendResult(interp, "bad state value \"", mbPtr->state,
  602.             "\":  must be normal, active, or disabled", (char *) NULL);
  603.         mbPtr->state = tkNormalUid;
  604.         return TCL_ERROR;
  605.     }
  606.     }
  607.  
  608.     gcValues.font = mbPtr->fontPtr->fid;
  609.     gcValues.foreground = mbPtr->normalFg->pixel;
  610.     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
  611.  
  612.     /*
  613.      * Note: GraphicsExpose events are disabled in GC's because they're
  614.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  615.      * that there's no problem with obscured areas).
  616.      */
  617.  
  618.     gcValues.graphics_exposures = False;
  619.     newGC = Tk_GetGC(mbPtr->tkwin,
  620.         GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
  621.     if (mbPtr->normalTextGC != None) {
  622.     Tk_FreeGC(mbPtr->normalTextGC);
  623.     }
  624.     mbPtr->normalTextGC = newGC;
  625.  
  626.     gcValues.font = mbPtr->fontPtr->fid;
  627.     gcValues.foreground = mbPtr->activeFg->pixel;
  628.     gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel;
  629.     newGC = Tk_GetGC(mbPtr->tkwin, GCForeground|GCBackground|GCFont,
  630.         &gcValues);
  631.     if (mbPtr->activeTextGC != None) {
  632.     Tk_FreeGC(mbPtr->activeTextGC);
  633.     }
  634.     mbPtr->activeTextGC = newGC;
  635.  
  636.     gcValues.font = mbPtr->fontPtr->fid;
  637.     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
  638.     if (mbPtr->disabledFg != NULL) {
  639.     gcValues.foreground = mbPtr->disabledFg->pixel;
  640.     mask = GCForeground|GCBackground|GCFont;
  641.     } else {
  642.     gcValues.foreground = gcValues.background;
  643.     if (mbPtr->gray == None) {
  644.         mbPtr->gray = Tk_GetBitmap(interp, mbPtr->tkwin,
  645.             Tk_GetUid("gray50"));
  646.         if (mbPtr->gray == None) {
  647.         return TCL_ERROR;
  648.         }
  649.     }
  650.     gcValues.fill_style = FillStippled;
  651.     gcValues.stipple = mbPtr->gray;
  652.     mask = GCForeground|GCFillStyle|GCStipple;
  653.     }
  654.     newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
  655.     if (mbPtr->disabledGC != None) {
  656.     Tk_FreeGC(mbPtr->disabledGC);
  657.     }
  658.     mbPtr->disabledGC = newGC;
  659.  
  660.     if (mbPtr->padX < 0) {
  661.     mbPtr->padX = 0;
  662.     }
  663.     if (mbPtr->padY < 0) {
  664.     mbPtr->padY = 0;
  665.     }
  666.  
  667.     /*
  668.      * Set up a trace on the menu button's variable, then initialize
  669.      * the variable if it doesn't already exist, so that it can be
  670.      * accessed immediately from Tcl code without fear of
  671.      * "nonexistent variable" errors.
  672.      */
  673.  
  674.     value = Tcl_GetVar(interp, mbPtr->varName, TCL_GLOBAL_ONLY);
  675.     if (value == NULL) {
  676.     Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY);
  677.     }
  678.     Tcl_TraceVar(interp, mbPtr->varName,
  679.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  680.         MenuButtonVarProc, (ClientData) mbPtr);
  681.  
  682.     /*
  683.      * Set up a trace on the variable that determines what's displayed
  684.      * in the menu button, if such a trace has been requested.
  685.      */
  686.  
  687.     if ((mbPtr->bitmap == None) && (mbPtr->textVarName != NULL)) {
  688.     char *value;
  689.  
  690.     value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
  691.     if (value == NULL) {
  692.         Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
  693.             TCL_GLOBAL_ONLY);
  694.     } else {
  695.         if (mbPtr->text != NULL) {
  696.         ckfree(mbPtr->text);
  697.         }
  698.         mbPtr->text = ckalloc((unsigned) (strlen(value) + 1));
  699.         strcpy(mbPtr->text, value);
  700.     }
  701.     Tcl_TraceVar(interp, mbPtr->textVarName,
  702.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  703.         MenuButtonTextVarProc, (ClientData) mbPtr);
  704.     }
  705.  
  706.     /*
  707.      * Recompute the geometry for the button.
  708.      */
  709.  
  710.     ComputeMenuButtonGeometry(mbPtr);
  711.  
  712.     /*
  713.      * Lastly, arrange for the button to be redisplayed.
  714.      */
  715.  
  716.     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
  717.     Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  718.     mbPtr->flags |= REDRAW_PENDING;
  719.     }
  720.  
  721.     return TCL_OK;
  722. }
  723.  
  724. /*
  725.  *----------------------------------------------------------------------
  726.  *
  727.  * DisplayMenuButton --
  728.  *
  729.  *    This procedure is invoked to display a menubutton widget.
  730.  *
  731.  * Results:
  732.  *    None.
  733.  *
  734.  * Side effects:
  735.  *    Commands are output to X to display the menubutton in its
  736.  *    current mode.
  737.  *
  738.  *----------------------------------------------------------------------
  739.  */
  740.  
  741. static void
  742. DisplayMenuButton(clientData)
  743.     ClientData clientData;    /* Information about widget. */
  744. {
  745.     register MenuButton *mbPtr = (MenuButton *) clientData;
  746.     GC gc;
  747.     Tk_3DBorder border;
  748.     Pixmap pixmap;
  749.     int x = 0;            /* Initialization needed only to stop
  750.                  * compiler warning. */
  751.     int y;
  752.     register Tk_Window tkwin = mbPtr->tkwin;
  753.  
  754.     mbPtr->flags &= ~REDRAW_PENDING;
  755.     if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  756.     return;
  757.     }
  758.  
  759.     if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) {
  760.     gc = mbPtr->disabledGC;
  761.     border = mbPtr->normalBorder;
  762.     } else if (mbPtr->state == tkActiveUid) {
  763.     gc = mbPtr->activeTextGC;
  764.     border = mbPtr->activeBorder;
  765.     } else {
  766.     gc = mbPtr->normalTextGC;
  767.     border = mbPtr->normalBorder;
  768.     }
  769.  
  770.     /*
  771.      * In order to avoid screen flashes, this procedure redraws
  772.      * the menu button in a pixmap, then copies the pixmap to the
  773.      * screen in a single operation.  This means that there's no
  774.      * point in time where the on-sreen image has been cleared.
  775.      */
  776.  
  777.     pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  778.         Tk_Width(tkwin), Tk_Height(tkwin),
  779.         DefaultDepthOfScreen(Tk_Screen(tkwin)));
  780.     Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
  781.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  782.  
  783.     /*
  784.      * Display bitmap or text for button.
  785.      */
  786.  
  787.     if (mbPtr->bitmap != None) {
  788.     unsigned int width, height;
  789.  
  790.     Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height);
  791.     switch (mbPtr->anchor) {
  792.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  793.         x += mbPtr->borderWidth + mbPtr->padX;
  794.         break;
  795.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  796.         x += (Tk_Width(tkwin) - width)/2;
  797.         break;
  798.         default:
  799.         x += Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX
  800.             - width;
  801.         break;
  802.     }
  803.     switch (mbPtr->anchor) {
  804.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  805.         y = mbPtr->borderWidth + mbPtr->padY;
  806.         break;
  807.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  808.         y = (Tk_Height(tkwin) - height)/2;
  809.         break;
  810.         default:
  811.         y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY
  812.             - height;
  813.         break;
  814.     }
  815.     XCopyPlane(Tk_Display(tkwin), mbPtr->bitmap, pixmap,
  816.         gc, 0, 0, width, height, x, y, 1);
  817.     } else {
  818.     switch (mbPtr->anchor) {
  819.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  820.         x = mbPtr->borderWidth + mbPtr->padX + mbPtr->leftBearing;
  821.         break;
  822.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  823.         x = (Tk_Width(tkwin) + mbPtr->leftBearing
  824.             - mbPtr->rightBearing)/2;
  825.         break;
  826.         default:
  827.         x = Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX
  828.             - mbPtr->rightBearing;
  829.         break;
  830.     }
  831.     switch (mbPtr->anchor) {
  832.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  833.         y = mbPtr->borderWidth + mbPtr->fontPtr->ascent
  834.             + mbPtr->padY;
  835.         break;
  836.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  837.         y = (Tk_Height(tkwin) + mbPtr->fontPtr->ascent
  838.             - mbPtr->fontPtr->descent)/2;
  839.         break;
  840.         default:
  841.         y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY
  842.             - mbPtr->fontPtr->descent;
  843.         break;
  844.     }
  845.     XDrawString(Tk_Display(tkwin), pixmap, gc, x, y, mbPtr->text,
  846.         mbPtr->textLength);
  847.     if (mbPtr->underline >= 0) {
  848.         TkUnderlineChars(Tk_Display(tkwin), pixmap, gc, mbPtr->fontPtr,
  849.             mbPtr->text, x, y, TK_NEWLINES_NOT_SPECIAL,
  850.             mbPtr->underline, mbPtr->underline);
  851.     }
  852.     }
  853.  
  854.     /*
  855.      * If the menu button is disabled with a stipple rather than a special
  856.      * foreground color, generate the stippled effect.
  857.      */
  858.  
  859.     if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg == NULL)) {
  860.     XFillRectangle(Tk_Display(tkwin), pixmap, mbPtr->disabledGC,
  861.         mbPtr->borderWidth, mbPtr->borderWidth,
  862.         (unsigned) (Tk_Width(tkwin) - 2*mbPtr->borderWidth),
  863.         (unsigned) (Tk_Height(tkwin) - 2*mbPtr->borderWidth));
  864.     }
  865.  
  866.     /*
  867.      * Draw the border last.  This way, if the menu button's contents
  868.      * overflow onto the border they'll be covered up by the border.
  869.      */
  870.  
  871.     if (mbPtr->relief != TK_RELIEF_FLAT) {
  872.     Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border,
  873.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  874.         mbPtr->borderWidth, mbPtr->relief);
  875.     }
  876.  
  877.     /*
  878.      * Copy the information from the off-screen pixmap onto the screen,
  879.      * then delete the pixmap.
  880.      */
  881.  
  882.     XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
  883.     mbPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  884.     XFreePixmap(Tk_Display(tkwin), pixmap);
  885. }
  886.  
  887. /*
  888.  *--------------------------------------------------------------
  889.  *
  890.  * MenuButtonEventProc --
  891.  *
  892.  *    This procedure is invoked by the Tk dispatcher for various
  893.  *    events on buttons.
  894.  *
  895.  * Results:
  896.  *    None.
  897.  *
  898.  * Side effects:
  899.  *    When the window gets deleted, internal structures get
  900.  *    cleaned up.  When it gets exposed, it is redisplayed.
  901.  *
  902.  *--------------------------------------------------------------
  903.  */
  904.  
  905. static void
  906. MenuButtonEventProc(clientData, eventPtr)
  907.     ClientData clientData;    /* Information about window. */
  908.     XEvent *eventPtr;        /* Information about event. */
  909. {
  910.     MenuButton *mbPtr = (MenuButton *) clientData;
  911.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  912.     if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
  913.         Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  914.         mbPtr->flags |= REDRAW_PENDING;
  915.     }
  916.     } else if (eventPtr->type == DestroyNotify) {
  917.     Tcl_DeleteCommand(mbPtr->interp, Tk_PathName(mbPtr->tkwin));
  918.  
  919.     /*
  920.      * Careful!  Must delete the event-sharing information here
  921.      * rather than in DestroyMenuButton.  By the time that procedure
  922.      * is called the tkwin may have been reused, resulting in some
  923.      * other window accidentally being cut off from shared events.
  924.      */
  925.  
  926.     Tk_UnshareEvents(mbPtr->tkwin, mbPtr->varName);
  927.     mbPtr->tkwin = NULL;
  928.     if (mbPtr->flags & REDRAW_PENDING) {
  929.         Tk_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr);
  930.     }
  931.     Tk_EventuallyFree((ClientData) mbPtr, DestroyMenuButton);
  932.     }
  933. }
  934.  
  935. /*
  936.  *----------------------------------------------------------------------
  937.  *
  938.  * ComputeMenuButtonGeometry --
  939.  *
  940.  *    After changes in a menu button's text or bitmap, this procedure
  941.  *    recomputes the menu button's geometry and passes this information
  942.  *    along to the geometry manager for the window.
  943.  *
  944.  * Results:
  945.  *    None.
  946.  *
  947.  * Side effects:
  948.  *    The menu button's window may change size.
  949.  *
  950.  *----------------------------------------------------------------------
  951.  */
  952.  
  953. static void
  954. ComputeMenuButtonGeometry(mbPtr)
  955.     register MenuButton *mbPtr;        /* Widget record for menu button. */
  956. {
  957.     XCharStruct bbox;
  958.     int dummy;
  959.     unsigned int width, height;
  960.  
  961.     if (mbPtr->bitmap != None) {
  962.     Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height);
  963.     if (mbPtr->width > 0) {
  964.         width = mbPtr->width;
  965.     }
  966.     if (mbPtr->height > 0) {
  967.         height = mbPtr->height;
  968.     }
  969.     } else {
  970.     mbPtr->textLength = strlen(mbPtr->text);
  971.     XTextExtents(mbPtr->fontPtr, mbPtr->text, mbPtr->textLength,
  972.         &dummy, &dummy, &dummy, &bbox);
  973.     mbPtr->leftBearing = bbox.lbearing;
  974.     mbPtr->rightBearing = bbox.rbearing;
  975.     width = bbox.lbearing + bbox.rbearing;
  976.     height = mbPtr->fontPtr->ascent + mbPtr->fontPtr->descent;
  977.     if (mbPtr->width > 0) {
  978.         width = mbPtr->width * XTextWidth(mbPtr->fontPtr, "0", 1);
  979.     }
  980.     if (mbPtr->height > 0) {
  981.         height *= mbPtr->height;
  982.     }
  983.     }
  984.  
  985.     width += 2*mbPtr->padX;
  986.     height += 2*mbPtr->padY;
  987.     Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->borderWidth),
  988.         (int) (height + 2*mbPtr->borderWidth));
  989.     Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->borderWidth);
  990. }
  991.  
  992. /*
  993.  *--------------------------------------------------------------
  994.  *
  995.  * MenuButtonVarProc --
  996.  *
  997.  *    This procedure is invoked when someone changes the
  998.  *    state variable associated with a menubutton.  This causes
  999.  *    the posted/unposted state of the menu to change if needed
  1000.  *    to match the variable's new value.
  1001.  *
  1002.  * Results:
  1003.  *    NULL is always returned.
  1004.  *
  1005.  * Side effects:
  1006.  *    The menu may be posted or unposted.
  1007.  *
  1008.  *--------------------------------------------------------------
  1009.  */
  1010.  
  1011.     /* ARGSUSED */
  1012. static char *
  1013. MenuButtonVarProc(clientData, interp, name1, name2, flags)
  1014.     ClientData clientData;    /* Information about button. */
  1015.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1016.     char *name1;        /* First part of variable's name. */
  1017.     char *name2;        /* Second part of variable's name. */
  1018.     int flags;            /* Describes what's happening to variable. */
  1019. {
  1020.     register MenuButton *mbPtr = (MenuButton *) clientData;
  1021.     char *value;
  1022.     int newFlags;
  1023.  
  1024.     /*
  1025.      * If the variable is being unset, then just re-establish the
  1026.      * trace unless the whole interpreter is going away.  Also unpost
  1027.      * the menu.
  1028.      */
  1029.  
  1030.     newFlags = mbPtr->flags;
  1031.     if (flags & TCL_TRACE_UNSETS) {
  1032.     newFlags &= ~POSTED;
  1033.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1034.         Tcl_TraceVar2(interp, name1, name2,
  1035.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1036.             MenuButtonVarProc, clientData);
  1037.     }
  1038.     } else {
  1039.  
  1040.     /*
  1041.      * Use the value of the variable to update the posted status of
  1042.      * the menu.
  1043.      */
  1044.     
  1045.     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
  1046.     if (strcmp(value, Tk_PathName(mbPtr->tkwin)) == 0) {
  1047.         newFlags |= POSTED;
  1048.     } else {
  1049.         newFlags &= ~POSTED;
  1050.     }
  1051.     }
  1052.  
  1053.     if ((mbPtr->menuName != NULL) && (newFlags != mbPtr->flags)) {
  1054.     mbPtr->flags = newFlags;
  1055.     if (newFlags & POSTED) {
  1056.         int x, y;
  1057.         char string[50];
  1058.  
  1059.         /*
  1060.          * Post the menu just below the menu button.
  1061.          */
  1062.  
  1063.         Tk_GetRootCoords(mbPtr->tkwin, &x, &y);
  1064.         y += Tk_Height(mbPtr->tkwin);
  1065.         sprintf(string, "%d %d ", x, y);
  1066.         if (Tcl_VarEval(interp, mbPtr->menuName, " post ", string,
  1067.             mbPtr->varName, (char *) NULL) != TCL_OK) {
  1068.         TkBindError(interp);
  1069.         }
  1070.     } else {
  1071.         if (Tcl_VarEval(interp, mbPtr->menuName, " unpost",
  1072.             (char *) NULL) != TCL_OK) {
  1073.         TkBindError(interp);
  1074.         }
  1075.     }
  1076.     }
  1077.     return (char *) NULL;
  1078. }
  1079.  
  1080. /*
  1081.  *--------------------------------------------------------------
  1082.  *
  1083.  * MenuButtonTextVarProc --
  1084.  *
  1085.  *    This procedure is invoked when someone changes the variable
  1086.  *    whose contents are to be displayed in a menu button.
  1087.  *
  1088.  * Results:
  1089.  *    NULL is always returned.
  1090.  *
  1091.  * Side effects:
  1092.  *    The text displayed in the menu button will change to match the
  1093.  *    variable.
  1094.  *
  1095.  *--------------------------------------------------------------
  1096.  */
  1097.  
  1098.     /* ARGSUSED */
  1099. static char *
  1100. MenuButtonTextVarProc(clientData, interp, name1, name2, flags)
  1101.     ClientData clientData;    /* Information about button. */
  1102.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1103.     char *name1;        /* Name of variable. */
  1104.     char *name2;        /* Second part of variable name. */
  1105.     int flags;            /* Information about what happened. */
  1106. {
  1107.     register MenuButton *mbPtr = (MenuButton *) clientData;
  1108.     char *value;
  1109.  
  1110.     /*
  1111.      * If the variable is unset, then immediately recreate it unless
  1112.      * the whole interpreter is going away.
  1113.      */
  1114.  
  1115.     if (flags & TCL_TRACE_UNSETS) {
  1116.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1117.         Tcl_SetVar2(interp, name1, name2, mbPtr->text,
  1118.             flags & TCL_GLOBAL_ONLY);
  1119.         Tcl_TraceVar2(interp, name1, name2,
  1120.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1121.             MenuButtonTextVarProc, clientData);
  1122.     }
  1123.     return (char *) NULL;
  1124.     }
  1125.  
  1126.     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
  1127.     if (value == NULL) {
  1128.     value = "";
  1129.     }
  1130.     if (mbPtr->text != NULL) {
  1131.     ckfree(mbPtr->text);
  1132.     }
  1133.     mbPtr->text = ckalloc((unsigned) (strlen(value) + 1));
  1134.     strcpy(mbPtr->text, value);
  1135.     ComputeMenuButtonGeometry(mbPtr);
  1136.  
  1137.     if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin)
  1138.         && !(mbPtr->flags & REDRAW_PENDING)) {
  1139.     Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  1140.     mbPtr->flags |= REDRAW_PENDING;
  1141.     }
  1142.     return (char *) NULL;
  1143. }
  1144.